package com.kd.service.email;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeUnit;

@Service
public class EmailCodeServiceImpl implements EmailCodeService {

    // 防护配置常量
    private static final int MAX_ATTEMPTS = 8;          // 最大验证尝试次数
    private static final int ATTEMPT_LOCK_HOURS = 1;    // 尝试失败锁定时间(小时)
    private static final int SEND_INTERVAL_SECONDS = 60; // 发送间隔(秒)
    private static final int IP_LIMIT_COUNT = 8;       // 单个IP每小时最大请求数
    private static final int IP_LIMIT_HOURS = 1;        // IP限制时间(小时)

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Value("${spring.mail.username}")
    private String fromEmail;

    @Value("${spring.mail.sender.name}")
    private String senderName;

    @Value("${email.code.expire:5}")
    private int codeExpireMinutes;

    @Value("${email.notify.expire:5}")
    private int notifyExpireMinutes;

    /**
     * 发送验证码邮件（带完整防护）
     */
    @Override
    public boolean sendEmail(String email, String type, String ip) {
        // 1. 参数校验
        if (email == null || !email.matches("^\\w+@\\w+\\.\\w+$"))  {
            throw new IllegalArgumentException("邮箱格式不正确");
        }

        // 2. IP限流检查
        checkIpLimit(ip);

        // 3. 发送频率检查
        String sendLimitKey = buildKey("send_limit", email);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(sendLimitKey)))  {
            Long remainTime = redisTemplate.getExpire(sendLimitKey);
            throw new RuntimeException("请求过于频繁，请" + remainTime + "秒后再试");
        }

        // 4. 生成并存储验证码
        String code = generateRandomCode();
        String codeKey = buildKey(type, email);

        redisTemplate.opsForValue().set(
                codeKey,
                code,
                "code".equals(type) ? codeExpireMinutes : notifyExpireMinutes,
                TimeUnit.MINUTES
        );

        // 5. 设置发送间隔限制
        redisTemplate.opsForValue().set(
                sendLimitKey,
                "1",
                SEND_INTERVAL_SECONDS,
                TimeUnit.SECONDS
        );

        // 6. 发送邮件
        try {
            sendEmailInternal(email, type, code);
            return true;
        } catch (Exception e) {
            redisTemplate.delete(codeKey);
            throw new RuntimeException("邮件发送失败", e);
        }
    }

    /**
     * 验证验证码（带防爆破）
     */
    @Override
    public boolean verifyEmailCode(String email, String code) {
        // 1. 参数校验
        if (code == null || !code.matches("^\\d{6}$"))  {
            throw new IllegalArgumentException("验证码必须为6位数字");
        }
        System.out.println("222verifyEmailCode(email,  code):"+email+"~~~"+code);
        // 2. 检查尝试次数
        String attemptKey = buildKey("attempt", email);// 构建尝试次数的Redis键，格式示例：email:attempt:user@example.com
        Long attempts = redisTemplate.opsForValue().increment(attemptKey); // Redis原子性增加尝试次数（线程安全）
        redisTemplate.expire(attemptKey,  ATTEMPT_LOCK_HOURS, TimeUnit.HOURS);// 设置这个尝试次数记录的过期时间（1小时后自动清除）

        if (attempts > MAX_ATTEMPTS) {
            throw new RuntimeException("验证尝试次数过多，请" + ATTEMPT_LOCK_HOURS + "小时后再试");
        }

        // 3. 验证验证码
        String codeKey = buildKey("code", email);// 构建存储验证码的Redis键，格式示例：email:code:user@example.com
        String storedCode = redisTemplate.opsForValue().get(codeKey);// 从Redis获取之前发送给这个邮箱的验证码
        boolean isValid = storedCode != null && storedCode.equals(code);
        System.out.println("333storedCode+code:"+storedCode+"~~~"+code);
        // 4. 处理验证结果
        if (isValid) {
            redisTemplate.delete(codeKey);
            redisTemplate.delete(attemptKey);
        }

        return isValid;
    }

    // ========== 私有方法 ========== //

    /**
     * IP限流检查
     */
    private void checkIpLimit(String ipAddress) {
        String ipKey = buildKey("ip_limit", ipAddress);// 1. 构建Redis键：格式示例 "email:ip_limit:192.168.1.1"
        Long count = redisTemplate.opsForValue().increment(ipKey); // 2. 原子性增加该IP的计数器（线程安全操作）,如果key不存在会自动创建并设置为1
        redisTemplate.expire(ipKey,  IP_LIMIT_HOURS, TimeUnit.HOURS);// 3. 设置键的过期时间（自动清理过期数据）

        if (count > IP_LIMIT_COUNT) {
            throw new RuntimeException("当前请求过于频繁");
        }
    }

    /**
     * 发送邮件内部实现
     */
    private void sendEmailInternal(String email, String type, String code)
            throws MessagingException, UnsupportedEncodingException {

        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);

        helper.setFrom(fromEmail,  senderName);
        helper.setTo(email);

        if ("code".equals(type)) {
            helper.setSubject(" 【可多科技】您的验证码");
            helper.setText(buildEmailCodeContent(code),  true);
        } else {
            helper.setSubject(" 【可多科技】重要通知");
            helper.setText(buildEmailNotifyContent(code),  true);
        }

        mailSender.send(message);
    }

    /**
     * 生成随机6位验证码
     */
    private String generateRandomCode() {
        return String.format("%06d",  (int) (Math.random()  * 999999));
    }

    /**
     * 构建Redis Key
     */
    private String buildKey(String type, String identifier) {

        return String.format("email:%s:%s",  type, identifier);
    }

    /**
     * 验证码邮件模板
     */
    private String buildEmailCodeContent(String code) {
        return "<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;'>" +
                "<h2 style='color: #1890ff;'>验证码通知</h2>" +
                "<p>您的验证码为：<strong style='font-size: 20px; color: #ff4d4f;'>" + code + "</strong></p>" +
                "<p style='color: #999;'>此验证码仅用于身份验证，有效期" + codeExpireMinutes + "分钟</p>" +
                "</div>";
    }

    /**
     * 通知邮件模板
     */
    private String buildEmailNotifyContent(String content) {
        return "<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;'>" +
                "<h2 style='color: #1890ff;'>业务通知</h2>" +
                "<p>" + content + "</p>" +
                "<p style='color: #999;'>本邮件为系统自动发送，请勿直接回复</p>" +
                "</div>";
    }
}